package com.zjgsu.nftplatform.service.impl;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.zjgsu.nftplatform.client.BlockchainClient;
import com.zjgsu.nftplatform.entity.Nft;
import com.zjgsu.nftplatform.entity.User;
import com.zjgsu.nftplatform.mapper.NftMapper;
import com.zjgsu.nftplatform.mapper.UserMapper;
import com.zjgsu.nftplatform.pojo.*;
import com.zjgsu.nftplatform.service.BlockchainService;
import com.zjgsu.nftplatform.util.UserIdUtil;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author ahy231
 * @date 2022/4/27 19:04
 * @description
 */
@Service
public class BlockchainServiceImpl implements BlockchainService {

    private final NftMapper nftMapper;
    private final UserMapper userMapper;
    private final BlockchainClient blockchainClient;

    public BlockchainServiceImpl(NftMapper nftMapper, UserMapper userMapper, BlockchainClient blockchainClient) {
        this.nftMapper = nftMapper;
        this.userMapper = userMapper;
        this.blockchainClient = blockchainClient;
    }

    @Override
    public WorkContent nft(Long nftId) {
        Result result = blockchainClient.nft(nftId.toString());
        Assert.isTrue(result.getCode() == 2000, "nft信息请求失败");
        return JSON.parseObject((String) result.getData(), WorkContent.class);
    }

    @Override
    public TreeNode tree(Long nftId) {
        List<TreeNode> queue = new ArrayList<>();
        TreeNode self = TreeNode.builder().nftId(nftId.toString()).build();
        // 找到父作品
        queue.add(self);
        while (queue.size() != 0) {
            TreeNode kid = queue.get(0);
            queue.remove(0);
            Long kidId = Long.valueOf(kid.getNftId());
            Result result = blockchainClient.nft(String.valueOf(kidId));
            Assert.isTrue(result.getCode() == 2000, "请求nft失败");
            WorkContent workContent = JSON.parseObject((String) result.getData(), WorkContent.class);
            workContent.setParents(workContent.getParents() == null ? new ArrayList<>() : workContent.getParents());
            kid.setParents(workContent.getParents().stream().map(p -> TreeNode.builder().nftId(p).build()).collect(Collectors.toList()));
            kid.setChildren(kid.getChildren() == null ? new ArrayList<>() : kid.getChildren());
            queue.addAll(kid.getParents());
        }
        // 找到子作品
        queue.add(self);
        while (queue.size() != 0) {
            TreeNode parent = queue.get(0);
            queue.remove(0);
            Long parentId = Long.valueOf(parent.getNftId());
            Result result = blockchainClient.nft(String.valueOf(parentId));
            Assert.isTrue(result.getCode() == 2000, "请求nft失败");
            WorkContent workContent = JSON.parseObject((String) result.getData(), WorkContent.class);
            workContent.setChildren(workContent.getChildren() == null ? new ArrayList<>() : workContent.getChildren());
            parent.setParents(parent.getParents() == null ? new ArrayList<>() : parent.getParents());
            parent.setChildren(workContent.getChildren().stream().map(k -> TreeNode.builder().nftId(k).build()).collect(Collectors.toList()));
            queue.addAll(parent.getChildren());
        }
        return self;
    }

    @Override
    public String createPublication(String nftId, String dayTime, String password) {
        Nft nft = nftMapper.selectById(nftId);
        Assert.notNull(nft, "查询不到nft");
        long userId = UserIdUtil.getUserId();
        User user = userMapper.selectById(userId);
        String address = user.getAddress();
        Result result = blockchainClient.createPublication(nftId, dayTime, address, password);
        Assert.isTrue(result.getCode() == ResultCode.SUCCESS.getCode(), "请求创建出版权失败");
        return (String) result.getData(); // 返回交易哈希
    }

    @Override
    public List<String> searchPublication(Long nftId) {
        Result result = blockchainClient.searchPublication(nftId.toString());
        Assert.isTrue(result.getCode() == ResultCode.SUCCESS.getCode(), "请求查询出版权协议失败");
        String array = (String) result.getData();
        @SuppressWarnings("unchecked")
        List<String> list = JSON.parseObject(array, List.class);
        return list;
    }

    @Override
    public String stake(Integer ratio, Long pid, String password) {
        long userId = UserIdUtil.getUserId();
        User user = userMapper.selectById(userId);
        String address = user.getAddress();
        Result result = blockchainClient.setStake(pid.toString(), ratio.toString(), address, password);
        Assert.isTrue(result.getCode() == ResultCode.SUCCESS.getCode(), "请求设置分红比例失败");
        return (String) result.getData();
    }

    @Override
    public String price(Long price, Long pid, String password) {
        long userId = UserIdUtil.getUserId();
        User user = userMapper.selectById(userId);
        String address = user.getAddress();
        Result result = blockchainClient.setPrice(pid.toString(), price.toString(), address, password);
        Assert.isTrue(result.getCode() == ResultCode.SUCCESS.getCode(), "请求设置价格失败");
        return (String) result.getData();
    }

    @Override
    public Publication publication(Long pid) {
        Result result = blockchainClient.getPublicationById(pid.toString());
        Assert.isTrue(result.getCode() == ResultCode.SUCCESS.getCode(), "请求出版权协议失败");
        String data = (String) result.getData();
        Publication publication = JSON.parseObject(data, Publication.class);
        long nftId = Long.parseLong(publication.getCid());
        TreeNode node = tree(nftId);

        // 找到所有父作品作者
        Map<User, String> userPriceMap = new HashMap<>();
        Map<User, Integer> userStakeMap = new HashMap<>();
        List<TreeNode> nodes = new ArrayList<>();
        nodes.add(node);
        while (!nodes.isEmpty()) {
            TreeNode thisNode = nodes.get(0);

            String id = thisNode.getNftId();
            Nft nft = nftMapper.selectById(id);

            Long userId = nft.getUserId();
            User user = userMapper.selectById(userId);

            userPriceMap.put(user, "");
            userStakeMap.put(user, -1);
        }

        // 查询所有分红比例和价格
        userPriceMap.forEach((u, p) -> {
            String address = u.getAddress();
            Result result1 = blockchainClient.getPriceById(pid.toString(), address);
            Assert.isTrue(result1.getCode() == ResultCode.SUCCESS.getCode(), "请求价格失败");
            String price = (String) result1.getData();
            userPriceMap.put(u, price);
        });
        userStakeMap.forEach((u, r) -> {
            String address = u.getAddress();
            Result result1 = blockchainClient.getStakeById(pid.toString(), address);
            Assert.isTrue(result1.getCode() == ResultCode.SUCCESS.getCode(), "请求分红比例失败");
            Integer ratio = (Integer) result1.getData();
            userStakeMap.put(u, ratio);
        });

        // 转化为相应map格式
        Map<String, String> addressPriceMap = new HashMap<>();
        Map<String, Integer> addressStakeMap = new HashMap<>();
        userPriceMap.forEach((u, p) -> addressPriceMap.put(u.getAddress(), p));
        userStakeMap.forEach((u, r) -> addressStakeMap.put(u.getAddress(), r));

        // 设置分红比例和价格
        publication.setPrices(addressPriceMap);
        publication.setStakes(addressStakeMap);

        return publication;
    }

    @Override
    public String agreePublication(Long pid, String price, String password) {
        long userId = UserIdUtil.getUserId();
        User user = userMapper.selectById(userId);
        String address = user.getAddress();
        Result result = blockchainClient.agreePublication(pid.toString(), price, address, password);
        Assert.isTrue(result.getCode() == ResultCode.SUCCESS.getCode(), "请求同意出版权失败");
        return (String) result.getData();
    }
}
